<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>用focusin,focusout事件代理的实时捕捉</title>
<style>
*{margin:0;padding:0;}
p span{color:#f00;}
div{height:100px;}
input{border:1px solid #ccc;}
textarea{border:1px solid #ccc;}
#bigdiv{height:800px;}
#input{ime-mode:disabled;}
</style>
</head>

<body>
<div><input type="text" id="input" value="" placeholder="我这里只能输入数字" /></div>
<div id="emailwrap"><input class="input" type="email" id="email" value="" style="width:200px;" placeholder="email" /><span id="sametxt"></span><input class="input" type="text" value="" style="width:100px;" placeholder="email" /></div>
<div><input type="password" id="pass" value="" style="width:200px" placeholder="密码框" /></div>
<div><textarea id="tarea" placeholder="文本域" maxlength="10"></textarea></div>
<p>您还可以输入<span id="maxlen">10</span>个字符</p>
<script>
/**********************************************
	@function	用户实时输入捕捉的直接和代理实现
				fixed oncontextmenu delete or cut and backspace
	@param		id: 必选参数，元素的id或dom对象
	@param		simpselector: 可选参数，id对应dom下的子元素简单选择器.class，将simpselector替换为fn回调函数就实现了重载，直接给元素添加添加捕捉实时输入事件
	@param		fn: 实时响应的回调函数
	@return		通过id获取的元素
	@note		原理说明：IE6-9通过focusein和focusout在输入框聚焦时绑定selectionchange、onpropertychange事件实现实时捕捉，更高级浏览器用事件捕获阶段绑定input事件
	@inadequate	不足：IE9同一时刻可能会有双重事件执行，因为有可能propertychange与selectionchange事件一同发生
************************************************/
function watchVal(id, simpselector, fn){
	var fn = fn || function(e){},
		obj = /INPUT|TEXTAREA/i.test(id.nodeName) ? id : id.nodeName ? id : document.getElementById(id),
		IE69 = !('placeholder' in document.createElement('INPUT')),
		IE9 = IE69 && window.addEventListener,
		simpselector = typeof simpselector === 'string' && simpselector.replace('.',''),
		clazzReg = new RegExp('(\\s|^)' + simpselector +'(\\s|$)'),
		testTarget = function(t){
			return /INPUT|TEXTAREA/i.test(t.tagName) && clazzReg.test(t.className);
		};
	
	if(typeof simpselector === 'function'){
		//实时输入的直接实现
		fn = simpselector;
		if(IE69){
			var IEwatchChange = function(e){
				var e = window.event;
				if(e.type == 'selectionchange' || e.propertyName == "value"){
					fn.call(obj, e);
				}
			}
			obj.attachEvent("onfocus", function(e){
				obj.attachEvent("onpropertychange", IEwatchChange);
			});
			obj.attachEvent("onblur", function(e){
				obj.detachEvent("onpropertychange", IEwatchChange);
			});
			obj.detachEvent("onselectionchange", IEwatchChange);
		}else{
			obj.addEventListener("input", fn, false);
			obj.addEventListener("drop", fn, false);
		}
	}else{
		//实时输入的代理实现
		if(IE69){
			var IEwatchChange = function(){
				var e = window.event, _this = null;
				if(e.propertyName && e.propertyName == "value"){
					_this = e.srcElement;
				}else if(e.type == 'selectionchange'){
					_this = document.activeElement;
				}
				//事件修复
				e.target = _this;
				fn.call(_this, e);
			}
			obj.attachEvent("onfocusin", function(e){
				var e = window.event, t = e.srcElement;
				if(testTarget(t)){
					t.attachEvent("onpropertychange", IEwatchChange);
				}
			});
			obj.attachEvent("onfocusout", function(e){
				var e = window.event, t = e.srcElement;
				if(testTarget(t)){
					t.detachEvent("onpropertychange", IEwatchChange);
				}
			});
			document.attachEvent("onselectionchange", function(){
				var t = document.activeElement, e = window.event;
				if(testTarget(t)){
					e.target = t;
					fn.call(t, e);
				}
			});
		}else{
			obj.addEventListener("focus", function(e){
				var t = e.target;
				if(testTarget(t)){
					t.addEventListener("input", fn, false);
				}
			}, true);
			obj.addEventListener("blur", function(e){
				var t = e.target;
				if(testTarget(t)){
					t.removeEventListener("input", fn, false);
				}
			}, true);
			document.addEventListener("drop", function(e){
				var t = e.target;
				if(testTarget(t)){
					e.target = t;
					fn.call(t, e);
				}
			}, false);
		}
	}
	return obj;
}

//测试用例demo，所有浏览器都兼容

//纯数字输入框
watchVal('input', function(e){
	var oldVal = this.value;
	if(/\D/g.test(oldVal)){
		console.dir(this);
		this.value = oldVal.replace(/\D/g, '');
	}
});
var sametxt = document.getElementById("sametxt");
/*watchVal(document.getElementById('emailwrap'), '.input', function(e){
	console.dir(this);
	var oldVal = this.value || this.defaultValue;
	if(/\D/g.test(oldVal)){
		this.value = oldVal.replace(/\D/g, '');
		sametxt.innerHTML = this.value;
	}
});*/

//实时响应用户输入，提示用户还能输入多少字符
var maxlen = document.getElementById('maxlen');
/*bind(document.getElementById('tarea'), function(e){
	maxlen.innerHTML = parseInt(this.getAttributeNode('maxlength').nodeValue) - this.value.length;
});*/
/*
//中文字符限制版：中文在谷歌和opera浏览器下会把输入的字符码直接输入到输入框，导致上面实时响应有负值的情况
var maxlenObj = document.getElementById('tarea'), maxLen = parseInt(maxlenObj.getAttributeNode("maxlength").nodeValue), counter = document.getElementById('maxlen');
bind(maxlenObj, function(e){
	var text = this.value || this.defaultValue || '', len = text.length,
	overplus = maxLen - len;
	if( overplus < 0 ){
		this.value = text.slice(0, maxLen);
		overplus = 0;
	}
	counter.innerHTML = overplus;
});
*/
//密码框测试
//bind('pass');
//低版本不支持type=email,number等类型的输入框测试
//bind('email');
</script>
</body>
</html>
